﻿using Unity.Mathematics;
using UnityEngine;
#if ENABLE_INPUT_SYSTEM
using UnityEngine.InputSystem;
using UnityEngine.InputSystem.EnhancedTouch;
using Touch = UnityEngine.InputSystem.EnhancedTouch.Touch;
#endif

namespace Jinndev.GameKit {

    /// <summary>
    /// 试图管理器，负责视角的缩放和位移
    /// </summary>
    public class ViewManager : MonoBehaviour {

        #region static
        public static readonly string PATH = "Jinndev/Prefab/View/ViewManager";
        public static ViewManager Instance { get; private set; }
        public static ViewManager Create() {
            return Instance != null ? Instance : CommonUtil.Instantiate<ViewManager>(PATH);
        }
        #endregion

        public enum ZoomMethod {
            OrthographicSize,
            FieldOfView
        }
        [System.Serializable]
        public class ZoomParams {
            [Tooltip("最小缩放值")]
            [Range(0, 100)]
            public float minZoom;
            [Tooltip("最大缩放值")]
            [Range(0, 150)]
            public float maxZoom;
            [Tooltip("缩放速度")]
            [Range(0, 100)]
            public float zoomSpeed;
            [Tooltip("缩放平滑")]
            [Range(0, 1)]
            public float zoomSmooth;
        }

        public Camera ActiveCamera { get; private set; }
        public bool Initialized { get; private set; }

        [Tooltip("输入总开关")]
        public bool enableInput = true;
        [Tooltip("是否使用新输入系统")]
        public bool useNewInputSystem = true;
        [Tooltip("移动开关")]
        public bool enableMove = false;
        [Tooltip("缩放开关")]
        public bool enableZoom = false;

        [Tooltip("缩放方式")]
        public ZoomMethod zoomMethod = ZoomMethod.OrthographicSize;
        [Tooltip("缩放参数")]
        public ZoomParams zoomParams = new ZoomParams {
            minZoom = 3f,
            maxZoom = 15f,
            zoomSpeed = 0.5f,
            zoomSmooth = 0.25f,
        };

        [Tooltip("移动平滑")]
        [Range(0, 1)]
        public float moveSmooth = 0.25f;

        public float2 PointerScreenPosition { get; private set; }
        public float3 PointerWorldPosition { get; private set; }
        public float WorldScreenRatio { get; private set; }

        private Transform cameraTransform;
        private float targetZoom;
        private Vector3 targetPosition;
        private float initZ;
        private bool focus = true;

#if ENABLE_INPUT_SYSTEM
        private Mouse mouse;
        private Pointer pointer;
#endif

        private bool multiTouch = false;
        private float multiTouchDistance;

        private void Awake() {
            Instance = this;
        }

        private void OnDestroy() {
            Instance = null;
        }

        public void Init(Camera activeCamera) {
            ActiveCamera = activeCamera;

            cameraTransform = activeCamera.transform;
            if (zoomMethod == ZoomMethod.OrthographicSize) {
                targetZoom = Mathf.Clamp(activeCamera.orthographicSize, zoomParams.minZoom, zoomParams.maxZoom);
            }
            else {
                targetZoom = Mathf.Clamp(activeCamera.fieldOfView, zoomParams.minZoom, zoomParams.maxZoom);
            }
            targetPosition = cameraTransform.position;
            initZ = targetPosition.z;

#if ENABLE_INPUT_SYSTEM
                if (Mouse.current != null) {
                    mouse = Mouse.current;
                    pointer = Pointer.current;
                }
                else if (Touchscreen.current != null && !EnhancedTouchSupport.enabled) {
                    EnhancedTouchSupport.Enable();
                }
#endif

            Initialized = true;
        }

        public void Dispose() {
            Initialized = false;
            ActiveCamera = null;
            cameraTransform = null;
            Destroy(gameObject);
        }

        private void OnApplicationFocus(bool focus) {
            this.focus = focus;
        }

        private void Update() {
            if (!Initialized || !focus || cameraTransform == null) {
                return;
            }

            if (enableMove) {
                if (cameraTransform.position != targetPosition) {
                    cameraTransform.position = Vector3.Lerp(cameraTransform.position, targetPosition, moveSmooth);
                }
            }

            if (enableZoom) {
                if (zoomMethod == ZoomMethod.OrthographicSize) {
                    if (ActiveCamera.orthographicSize != targetZoom) {
                        ActiveCamera.orthographicSize = Mathf.Lerp(ActiveCamera.orthographicSize, targetZoom, zoomParams.zoomSmooth);
                    }
                }
                else {
                    if (ActiveCamera.fieldOfView != targetZoom) {
                        ActiveCamera.fieldOfView = Mathf.Lerp(ActiveCamera.fieldOfView, targetZoom, zoomParams.zoomSmooth);
                    }
                }
            }

            Vector2 screenPos = GetPorinterScreenPosition();
            Vector3 worldPos = ActiveCamera.ScreenToWorldPoint(screenPos);
            PointerScreenPosition = screenPos;
            PointerWorldPosition = worldPos;

            float delta = 100;
            Vector3 deltaWorldPos = worldPos - ActiveCamera.ScreenToWorldPoint(screenPos - new Vector2(delta, 0));
            WorldScreenRatio = deltaWorldPos.x / delta;

            // zoom
#if ENABLE_INPUT_SYSTEM
            if (Touchscreen.current != null) {
                var touches = Touch.activeTouches;
                if (!multiTouch) {
                    if (touches.Count >= 2) {
                        multiTouch = true;
                        multiTouchDistance = Vector2.Distance(touches[0].screenPosition, touches[1].screenPosition);
                    }
                }
                else {
                    if (touches.Count >= 2) {
                        float distance = Vector2.Distance(touches[0].screenPosition, touches[1].screenPosition);
                        OnZoom(distance - multiTouchDistance);
                        multiTouchDistance = distance;
                    }
                    else {
                        multiTouch = false;
                    }
                }
            }
            else if (mouse != null) {
                Vector2 scroll = mouse.scroll.ReadValue();
                if (scroll.y != 0) {
                    OnZoom(scroll.y);
                }
            }
#else
            if (Input.mousePresent) {
                float wheel = Input.GetAxis("Mouse ScrollWheel");
                if (wheel != 0) {
                    OnZoom(wheel);
                }
            }
            if (Input.touchSupported) {
                if (!multiTouch && Input.touchCount >= 2) {
                    multiTouch = true;
                    multiTouchDistance = Vector2.Distance(Input.touches[0].position, Input.touches[1].position);
                }
                else if (multiTouch) {
                    if (Input.touchCount >= 2) {
                        float distance = Vector2.Distance(Input.touches[0].position, Input.touches[1].position);
                        OnZoom(distance - multiTouchDistance);
                        multiTouchDistance = distance;
                    }
                    else {
                        multiTouch = false;
                    }
                }
            }
#endif
        }

        private Vector2 GetPorinterScreenPosition() {
#if ENABLE_INPUT_SYSTEM
                //if (mouse != null) {
                //    return mouse.position.ReadValue();
                //}
                //else if (touchscreen != null) {
                //    return touchscreen.position.ReadValue();
                //}
                //else {
                //    return Pointer.current.position.ReadValue();
                //}
                if (pointer != null) {
                    return pointer.position.ReadValue();
                }
                else {
                    return Vector2.zero;
                }
#else
            return Input.mousePosition;
#endif
        }

        public Vector2 GetPosition() {
            return cameraTransform.position;
        }

        // 立即移动到
        public void SetPosition(Vector2 position) {
            targetPosition = new Vector3(position.x, position.y, initZ);
            cameraTransform.position = targetPosition;
        }

        // 不立即移动到
        public void SetTargetPosition(Vector2 position) {
            targetPosition = new Vector3(position.x, position.y, initZ);
        }

        private void OnZoom(float delta) {
            if (!enableInput || !enableZoom) {
                return;
            }
            targetZoom = Mathf.Clamp(targetZoom - delta * zoomParams.zoomSpeed * Time.deltaTime, zoomParams.minZoom, zoomParams.maxZoom);
        }

    }
}
